package cn.texous.web.parsing.engine.common.utils;

import cn.texous.util.commons.util.GsonUtils;
import cn.texous.util.commons.util.StringUtils;
import cn.texous.web.parsing.engine.sdk.constants.RespType;
import cn.texous.web.parsing.engine.sdk.param.ParsingFieldInfo;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.seimicrawler.xpath.JXDocument;
import org.seimicrawler.xpath.JXNode;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
 * insert description here.
 *
 * @author Showa.L
 * @since 2020/3/13 11:57
 */
@Slf4j
public class ContentProcessorUtils {

    public static String process(String content, RespType contentType, List<ParsingFieldInfo> parsingFieldInfos) {
        long start = System.currentTimeMillis();
        // 解析内容
        Map<String, Object> parserContent = parserContent(content,
                contentType.getCode(), parsingFieldInfos);
        log.info("解析文章所花费的时间：{}", System.currentTimeMillis() - start);
        // 返回结果
        return GsonUtils.toJson(parserContent);
    }

    private static Map<String, Object> parserContent(String content, String contentType,
                                                     List<ParsingFieldInfo> parserFieldInfos) {
        try {
            if (RespType.JSON.getCode().equalsIgnoreCase(contentType)) {
                Map<String, Object> result = new HashMap<>();
                JsonNode jsonNode = new ObjectMapper().readTree(content);
                parserJsonContentByFields(result, parserFieldInfos, jsonNode);
                return result;
            } else {
                Document document = Jsoup.parse(content);
                JXDocument jxDocument = JXDocument.create(document);
                Map<String, Object> result = parserContentByFields(parserFieldInfos, jxDocument);
                return result;
            }
        } catch (IOException e) {
            log.info("解析结果出错：contentType={}, content={}", contentType, content);
        }
        return null;
    }

    private static void parserJsonContentByFields(Map<String, Object> result,
                                                  List<ParsingFieldInfo> parserFieldInfos,
                                                  JsonNode jsonNode) {
        for (ParsingFieldInfo parserFieldInfo : parserFieldInfos) {
            if (parserFieldInfo == null || StringUtils.isTrimEmpty(parserFieldInfo.getXpath()))
                continue;
            if (parserFieldInfo.isChangeNode()) {
                List<Map<String, Object>> cnds = new ArrayList<>();
                result.put(parserFieldInfo.getField(), cnds);
                JsonNode jsonNodes = jsonNode.get(parserFieldInfo.getXpath());
                jsonNodes.forEach(js -> changeJsonNode(js, parserFieldInfo.getChilds(), cnds));
            } else {
                JsonNode js = jsonNode.get(parserFieldInfo.getXpath());
                result.put(parserFieldInfo.getField(), js.textValue());
            }
        }
    }

    private static void changeJsonNode(JsonNode jsonNode,
                                       List<ParsingFieldInfo> parserFieldInfos,
                                       List<Map<String, Object>> childNodes) {
        Map<String, Object> item = new HashMap<>();
        childNodes.add(item);
        if (parserFieldInfos == null || parserFieldInfos.isEmpty())
            return;
        parserJsonContentByFields(item, parserFieldInfos, jsonNode);
    }

    private static Map<String, Object> parserContentByFields(List<ParsingFieldInfo> parserFieldInfos,
                                                             JXDocument jxDocument) {
        Iterator<ParsingFieldInfo> iterator = parserFieldInfos.iterator();
        Map<String, Object> result = new HashMap<>();
        while (iterator.hasNext()) {
            ParsingFieldInfo anaFieldInfo = iterator.next();
            if (StringUtils.isTrimEmpty(anaFieldInfo.getXpath()))
                continue;
            List<JXNode> jxNodes = jxDocument.selN(anaFieldInfo.getXpath());
            if (anaFieldInfo.isChangeNode()) {
                List<Map<String, Object>> childNodes = new ArrayList<>();
                result.put(anaFieldInfo.getField(), childNodes);
                jxNodes.forEach(p -> changeNode(p, anaFieldInfo.getChilds(), childNodes));
            } else {
                jxNodes.forEach(p -> result.put(anaFieldInfo.getField(), p.toString()));
            }

        }
        return result;
    }

    private static void changeNode(JXNode jxNode,
                                   List<ParsingFieldInfo> parserFieldInfos,
                                   List<Map<String, Object>> childNodes) {
        Map<String, Object> items = new HashMap<>();
        childNodes.add(items);
        if (parserFieldInfos == null || parserFieldInfos.isEmpty())
            return;
        for (int i = 0; i < parserFieldInfos.size(); i++) {
            ParsingFieldInfo parserFieldInfo = parserFieldInfos.get(i);
            if (StringUtils.isTrimEmpty(parserFieldInfo.getXpath()))
                continue;
            List<JXNode> jxNodes = jxNode.sel(parserFieldInfo.getXpath());
            if (parserFieldInfo.isChangeNode()) {
                List<Map<String, Object>> cnds = new ArrayList<>();
                items.put(parserFieldInfo.getField(), cnds);
                jxNodes.forEach(p -> changeNode(p, parserFieldInfo.getChilds(), cnds));
            } else {
                jxNodes.forEach(p -> items.put(parserFieldInfo.getField(), p.toString()));
            }
        }
    }

}
